Concept / Command | Explanation |
---|---|
Docker Engine |
Core software that manages images and containers; provides the Docker daemon, CLI client, and APIs. |
Dockerfile |
A text file containing instructions for building a Docker image. It specifies base images, layers, commands, etc. This is a recipe for creating an image. |
Docker Image |
A read-only template built from a Dockerfile. Images contain all dependencies your application needs. They are immutable, versioned, and layered. |
Docker Container |
A running instance of an image. Containers are isolated processes that run on the host OS using the image’s filesystem. They’re ephemeral by design. |
Difference: Dockerfile vs. Image vs. Container |
• Dockerfile: A set of instructions (recipe) for building an image. • Image: A static snapshot (read-only) resulting from the Dockerfile build. • Container: A running (or stopped) instance of an image, isolated but using the host OS kernel. |
docker build |
Builds a Docker image from a Dockerfile. Example: docker build -t myapp:1.0 . |
docker run |
Creates and runs a container from a specified image. Common flags: -d (detached), -p (port mapping), --name (container name). Example: docker run -d -p 80:80 --name web myapp:1.0 |
docker stop / start |
Stops or restarts a running container. docker stop <container> docker start <container> |
docker ps |
Lists running containers (use -a to see all). docker ps -a |
docker exec |
Runs a command within an existing container, often used to get a shell. Example: docker exec -it <container> bash |
docker logs |
Shows a container’s logs (use -f for “follow”). docker logs -f <container> |
docker rm |
Removes one or more containers. You must stop them first. docker rm <container> |
docker rmi |
Removes one or more images. docker rmi <image> |
docker pull / push |
Pulls or pushes an image to a remote registry (e.g., Docker Hub). You need to be logged in to push. docker pull <image> docker push <repository>/<image> |
docker tag |
Assigns a new name/tag to an image. docker tag <source_image> <repo>:<tag> |
docker compose |
Tool for defining and running multi-container Docker apps via a YAML file (docker-compose.yml). Allows linking services. |
Volume |
Persist data outside of container’s filesystem. Example: docker volume create data_vol docker run -v data_vol:/data myapp:1.0 |
Network |
Custom networks let containers talk to each other by name. Example: docker network create mynet docker run --network=mynet --name=db ... |
Best Practices |
- Use small base images (e.g., Alpine) for minimal footprint. - Leverage multi-stage builds for lighter final images. - Keep container processes short-lived (one main process if possible). - Utilize .dockerignore to reduce build context size. - Set explicit versions for dependencies. - Use caching effectively (order instructions from least-changing to most-changing). - Run as non-root if possible (USER directive). |
Here is an example of a Dockerfile built following best practices. You can copy it and use it to build any Dockerfile you need.
# syntax=docker/dockerfile:1.5
# This tells Docker which set of instructions (version 1.5) to use.
# Think of it as choosing the rules for our recipe.
# Use an ARG (argument) for the base image tag.
# This means you can change the Node.js version when you build the image.
ARG BASE_IMAGE_TAG=16-alpine
# "16-alpine" means we're using Node.js version 16 on a lightweight Alpine Linux system.
# Start with a Node.js image based on the tag above.
FROM node:${BASE_IMAGE_TAG}
# "FROM" tells Docker to begin with this pre-built Node.js image.
# It’s like starting with a ready-made box that already has Node.js installed.
# Add labels to the image for metadata.
# Labels act like stickers that show who made the image and what it’s for.
LABEL maintainer="your_email@example.com"
LABEL description="Example Node.js application container."
# (Tip: Use a .dockerignore file to exclude unnecessary files and speed up the build.)
# Update package lists and install extra tools needed for building your app.
RUN apk update && apk add --no-cache \
python3 \ # Installs Python 3, which may be needed for building some packages.
make \ # Installs 'make', a tool used to compile programs.
g++ \ # Installs the GNU C++ compiler for compiling native add-ons.
git # Installs Git to help fetch code from repositories.
# 'apk' is the package manager for Alpine Linux.
# Create a new user and group to run the application safely.
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 'addgroup -S' creates a system group named "appgroup".
# 'adduser -S' creates a system user named "appuser" and adds it to "appgroup".
USER appuser
# Running as a non-root user improves security.
# Set the working directory inside the container.
WORKDIR /app
# This is like saying, "let's work inside the /app folder from now on."
# Copy package files first to take advantage of caching.
COPY package*.json ./
# Only the package.json files are copied first.
# This helps Docker reuse this step later if these files haven't changed, making builds faster.
# Install Node.js dependencies.
RUN npm install --no-optional
# 'npm install' downloads and installs the libraries your app needs.
# The '--no-optional' flag skips extra packages that are not required.
# Copy the rest of the application code into the container.
COPY . /app
# This brings all your project files into the /app folder inside the container.
# (Optional) If your project needs to be built (like compiling code), you can add a build step here.
# For example: RUN npm run build
# Expose the port that your application will use.
EXPOSE 3000
# This tells Docker (and people reading the file) that your app listens on port 3000.
# Note: This does not publish the port; it's for documentation and linking.
# Define what happens when the container starts:
# ENTRYPOINT:
# - This sets the main command that will always run when the container starts.
# - It cannot be easily overridden by additional arguments unless you use the --entrypoint flag.
# CMD:
# - This provides default arguments for the ENTRYPOINT command.
# - If no arguments are passed when starting the container, CMD's values will be used.
# - However, if you pass arguments via the docker run command, those arguments override CMD,
# but the ENTRYPOINT remains in place.
# In this example:
# ENTRYPOINT ["node"] tells Docker to always run the Node.js executable.
# CMD ["index.js"] provides the default file to run.
# So, by default, the container will execute "node index.js".
# If you run the container with extra arguments (e.g., docker run server.js),
# the command will become "node server.js", overriding the CMD value.
ENTRYPOINT ["node"]
CMD ["index.js"]